
function SimpleMove(move, number) {
  this.number = number;
  this.state = move.state;
  this.splitDir = move.splitDir;
  this.splitStart = null;
  if (move.splitStart) {
    this.splitStart = move.splitStart;
  }
  this.splitEnd = null;
  if (move.splitEnd) {
    this.splitEnd = move.splitEnd;
  }

  this.splitLine = move.splitLine;
  this.mergeLeftTop = null;
  if (move.mergeLeftTop) {
    this.mergeLeftTop = move.mergeLeftTop.start;
  }
  this.mergeRightTop = null;
  if (move.mergeRightTop) {
    this.mergeRightTop = move.mergeRightTop.start;
  }
  this.mergeLeftBottom = null;
  if (move.mergeLeftBottom) {
    this.mergeLeftBottom = move.mergeLeftBottom.start;
  }
  this.mergeRightBottom = null;
  if (move.mergeRightBottom) {
    this.mergeRightBottom = move.mergeRightBottom.start;
  }
  this.swappedPieces = [];
  for (var i = 0; i < move.numSwappedPieces; i++) {
    this.swappedPieces.push(move.swappedPieces[i].start)
  }
}

var initFromSimpleMove = function(simpleMove, move, number) {
  assert(simpleMove.number == number);
  if (simpleMove.state == STATE_SPLIT) {
    // simple assignment doesn't work because it doesn't support methods
    var splitLine = new Rational(simpleMove.splitLine.top, simpleMove.splitLine.bot);

    var splitStart = new Rational(simpleMove.splitStart.top, simpleMove.splitStart.bot);
    var splitEnd = new Rational(simpleMove.splitEnd.top, simpleMove.splitEnd.bot);

    var splitFirst = null;
    var splitLast = null;
    if (simpleMove.splitDir == VERTICAL) {
      splitFirst = move.board.getPiece(splitLine.float(), splitStart.float() + TINY);
      splitLast = move.board.getPiece(splitLine.float(), splitEnd.float() - TINY);
    } else {
      splitFirst = move.board.getPiece(splitStart.float() + TINY, splitLine.float());
      splitLast = move.board.getPiece(splitEnd.float() - TINY, splitLine.float());
    }
    assert(splitFirst && splitLast);
    if (splitFirst == splitLast) {
      move.split(splitFirst, simpleMove.splitDir);
    } else {
      move.longSplit(splitFirst, splitLast, simpleMove.splitDir);
    }
  } else {
    mergeLeftTop = move.board.equiStart(simpleMove.mergeLeftTop);
    mergeRightTop = move.board.equiStart(simpleMove.mergeRightTop);
    mergeLeftBottom = move.board.equiStart(simpleMove.mergeLeftBottom);
    mergeRightBottom = move.board.equiStart(simpleMove.mergeRightBottom);
    move.merge(mergeLeftTop, mergeRightTop, mergeLeftBottom, mergeRightBottom);
  }
  for (var i = 0; i < simpleMove.swappedPieces.length; i++) {
    var p = move.board.equiStart(simpleMove.swappedPieces[i]);
    move.swap(p, UNKNOWN_SIDE);
  }
}

function Game (x, y, validator, whitePlayerComp, blackPlayerComp) {
  if (whitePlayerComp == undefined) whitePlayerComp = false;
  if (blackPlayerComp == undefined) blackPlayerComp = false;
  this.board = new Board(x, y);
  this.validator = validator;
  this.board.build();
  this.moves = new Array();
  this.startingColor = BLACK;
  this.moveId = 1;
  this.readOnly = false;
  this.player = new Array();
  this.player[WHITE] = whitePlayerComp;
  this.player[BLACK] = blackPlayerComp;
  this.gameOver = false;
  this.moveSelected = 0;
  this._setupForNextMove();
}

Game.prototype.getBoard = function() {
  if (this.moveSelected) {
    return this.moves[this.moveSelected].board;
  }
  return this.moves[this.moveId].board;
}

Game.prototype.getMoveId = function() {
  return this.moveId;
}

Game.prototype.getMessage = function() {
  return this.moves[this.moveId].message;
}

Game.prototype.setPlayerComp = function(color, isComputer) {
  if (this.gameOver) {
    this.moves[this.moveId].message = "Game over. Can't change player mode";
    return;
  }
  this.player[color] = isComputer;
  if (color == this.moves[this.moveId].color) {
    this.cancelMove();
  }
}

Game.prototype.setDemoMode = function(isDemo) {
  if (this.gameOver) {
    this.moves[this.moveId].message = "Game over. Can't change demo mode";
    return;
  }
  this.demo = isDemo;
  this.setPlayerComp(BLACK, true);
  this.setPlayerComp(WHITE, true);

  this.moves[this.moveId].message = "Demo mode";
  this._setupForNextMove();
}

Game.prototype.cancelMove = function() {
  if (this.gameOver) {
    this.moves[this.moveId].message = "Game over. Can not cancel move";
    return;
  }
  this.moves[this.moveId].message = "";
  this._setupForNextMove();
}

Game.prototype.submitMove = function() {
  if (this.gameOver) {
    this.moves[this.moveId].message = "Game over. Can not make move";
    return false;
  }

  var isComplete = this.moves[this.moveId].complete();
  if (!isComplete) {
    return false;
  }
  this.moves[this.moveId].message = "";
  this.moveId += 1;
  this._setupForNextMove();

  if (this.moves[this.moveId-1].winningColor != NULL_COLOR) {
    this.gameOver = true;
    this.moves[this.moveId].message = this.moves[this.moveId-1].message;
  }
  return true;
}

Game.prototype.undoLastMove= function() {
  this.gameOver = false;
  this.moves[this.moveId].message = "";
  if (this.moveId > 1) {
    this.moveId -= 1;
    this._setupForNextMove();
  } else {
    this.moves[this.moveId].message = "Can not undo - game just started";
  }
}

Game.prototype.getCurrentColor = function() {
  return this.moves[this.moveId].color;
}

Game.prototype.getLastPlayedColor = function() {
  return this.moves[this.moveId-1].color;
}

Game.prototype.getLastMoveRepresentation = function() {
  return new SimpleMove(this.moves[this.moveId-1], this.moveId-1);
}

Game.prototype.makeMoveFromRepresentation = function(simpleMove) {
  initFromSimpleMove(simpleMove, this.moves[this.moveId], this.moveId);
}

Game.prototype.getWinningColor = function() {
  return this.moves[this.moveId-1].winningColor;
}

Game.prototype.split = function(piece, dir) {
  if (this.gameOver) {
    this.moves[this.moveId].message = "Game over.";
    return;
  }
  if (this.readOnly) {
    this.moves[this.moveId].message = "Can not split. Waiting for your opponent to move...";
    return;
  }
  this.moves[this.moveId].split(piece, dir);
}

Game.prototype.splitOrSwap = function(piece, dir, side) {
  if (this.gameOver) {
    this.moves[this.moveId].message = "Game over.";
    return;
  }
  if (this.readOnly) {
    this.moves[this.moveId].message = "Waiting for your opponent to move...";
    return;
  }
  this.moves[this.moveId].splitOrSwap(piece, dir, side);
}


Game.prototype.merge = function(pLeftTop, pRightTop, pLeftBottom, pRightBottom) {
  if (this.gameOver) {
    this.moves[this.moveId].message = "Game over.";
    return;
  }
  if (this.readOnly) {
    this.moves[this.moveId].message = "Can not merge. Waiting for your opponent to move...";
    return;
  }
  this.moves[this.moveId].merge(pLeftTop, pRightTop, pLeftBottom, pRightBottom);
}

Game.prototype.longSplit = function(pFirst, pLast, dir) {
  if (this.gameOver) {
    this.moves[this.moveId].message = "Game over.";
    return;
  }
  if (this.readOnly) {
    this.moves[this.moveId].message = "Can not do long split. Waiting for your opponent to move...";
    return;
  }
  this.moves[this.moveId].longSplit(pFirst, pLast, dir);
}

Game.prototype.swap = function (piece, side) {
  if (this.gameOver) {
    this.moves[this.moveId].message = "Game over.";
    return;
  }
  if (this.readOnly) {
    this.moves[this.moveId].message = "Can not swap. Waiting for your opponent to move...";
    return;
  }
  this.moves[this.moveId].swap(piece, side);
}

Game.prototype.setMoveToView = function(moveSelected) {
  this.moveSelected = moveSelected;
}

Game.prototype._setupForNextMove = function() {
  if (this.moveId == 1) {
    this.moves[this.moveId] = new Move(this.board, this.startingColor, this.moveId, this.validator);
  } else {
    this.moves[this.moveId] = new Move(this.moves[this.moveId-1].board, OPPOSITE_COLOR[this.moves[this.moveId-1].color], this.moveId, this.validator);
  }
  var isComp = this.player[this.moves[this.moveId].color];
  if (isComp) {
    this.readOnly = true;
    var timeout = 5000;
    if (this.moves[this.moveId].color == WHITE) {
	  alg = new AlgDeep();
	  timeout = 2000;
	} else {
	  alg = new AlgCluster();
	}
	alg.makeMove(this.moves[this.moveId]);
	if (this.demo) {
	  setTimeout("_controller.submitMove()", timeout);
	}
  } else {
    this.readOnly = false;
  }
}

Game.prototype.getPieceActions = function(piece) {
   var move = this.moves[this.moveId];
   var pieceActions = new PieceActions();
   for (var dir = 0; dir < 2; dir++) {
     pieceActions.split[dir] = move.split(piece, dir, true);
   }
   pieceActions.swap = move.swap(piece, UNKNOWN_SIDE, true);
   return pieceActions;
}

toTest.push(Game);


Game.prototype.test = function() {
}